package com.gitee.huanminabc.utils_tools.spring_base.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
@Slf4j
public class TransactionUtil {
    private static final ThreadLocal<Map<String, Set<String>>> threadLocal = ThreadLocal.withInitial(()->new HashMap<>(3));

    /**
     * 在当前事务中执行读操作，如果没有事务则直接执行
     * 适用于需要在事务中进行只读查询的场景
     * 兼容:
     * 1. A事务提交后, 我们事务也能读到A事务提交的数据
     * 2. 兼容自己事务多次操作同一个数据那么也能读取到最新数据
     *
     * @param key      当前操作的唯一标识, 一般使用表名+业务主键做唯一标识进行隔离
     * @param supplier 读取数据的操作
     * @param <T>      返回值类型
     * @return 执行结果
     * PAccount account = TransactionUtils.openReadTransactional(PAccount.class.getSimpleName()+accountId.toString(),() -> accountManager.getNormalAccountOrThrow(accountId));
     */
    public static <T> T openReadTransactional(String key, Supplier<T> supplier) {
        //如果当前线程没有事务，则直接执行
        boolean hasTransaction = TransactionSynchronizationManager.isActualTransactionActive();
        if (!hasTransaction) {
            return supplier.get();
        }
        //获取当前事务名称, 子事务会继承父事务的名称
        String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCompletion(int status) {
                //事务完成后清理
                threadLocal.get().remove(currentTransactionName);
                if (threadLocal.get().isEmpty()) {
                    threadLocal.remove();
                }
            }
        });
        Set<String> stringKeys = threadLocal.get().computeIfAbsent(currentTransactionName, k -> new HashSet<>(3));
        if (stringKeys.contains(key)) {
            return supplier.get();
        } else {
            stringKeys.add(key);

            PlatformTransactionManager platformTransactionManager = SpringContextUtil.getBean(PlatformTransactionManager.class);

            //开启事务 REQUIRES_NEW事务并且设置为 readOnly=true  然后读已提交
            // 1. 配置事务属性
            DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
            // 设置传播行为：REQUIRES_NEW（无论是否有外层事务，都创建新事务）
            transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            // 设置隔离级别：READ_COMMITTED（读已提交）
            transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
            // 设置只读属性：true（仅查询操作，优化性能）
            transactionDefinition.setReadOnly(true);
            TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
            try {
                return supplier.get();
            } finally {
                platformTransactionManager.commit(transactionStatus);
            }
        }
    }


    //开启一个独立的事务不影响父事务
    public static <T> T openSolitudeTransactional(Supplier<T> supplier) {
        PlatformTransactionManager platformTransactionManager = SpringContextUtil.getBean(PlatformTransactionManager.class);
        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        try {
            T t = supplier.get();
            platformTransactionManager.commit(transactionStatus);
            return t;
        } catch (Exception e) {
             platformTransactionManager.rollback(transactionStatus);
             throw e;
        }
    }

    /**
     * 部分回滚, openPartialRollbackTransactional失败不会影响父事务  但是父事务回滚时子事务也会回滚
     * 目前主流数据库都支持
     * @param supplier
     * @return
     * @param <T>
     */
    public static <T> T openPartialTransactional(Supplier<T> supplier) {
        PlatformTransactionManager platformTransactionManager = SpringContextUtil.getBean(PlatformTransactionManager.class);
        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        try {
            T t = supplier.get();
            platformTransactionManager.commit(transactionStatus);
            return t;
        } catch (Exception e) {
             platformTransactionManager.rollback(transactionStatus);
             throw e;
        }
    }


    //手动事务
    public  static void around(TransactionCallback r){
        PlatformTransactionManager platformTransactionManager= SpringContextUtil.getBean(PlatformTransactionManager.class);
         DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
         transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        //开启事务
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        try {
            r.run();
            //提交事务
            platformTransactionManager.commit(transactionStatus);
        }  catch (Throwable e) {
            //异常回调
            r.error(e);
            //回滚事务
            platformTransactionManager.rollback(transactionStatus);
        }finally {
            r.after();
        }
    }

    protected static   void errorLog(Throwable e){
        log.error("",e);
    }
}
